package com.mazaiting.servlet;

import com.mazaiting.annotation.Controller;
import com.mazaiting.annotation.RequestMapping;
import com.mazaiting.annotation.Service;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Servlet 分布中心
 *
 * @author mazaiting
 * @since 2021/10/15
 */
public class DispatcherServlet extends HttpServlet {
    /**
     * 类列表
     */
    private final List<String> classList = new ArrayList<>();
    /**
     * bean 实例
     */
    private final Map<String, Object> beanInstanceMap = new HashMap<>();
    /**
     * url 映射
     */
    private final Map<String, Method> urlHandlerMap = new HashMap<>();

    /**
     * servlet初始化方法
     * 1 scan扫描包路径
     * 2 构造所有类集合 (list<String>)
     * 3 构造所有bean实例集合 (beanName-> bean)
     * 4 构造所有请求handler跳转（uri -> method）
     */
    public void init() throws ServletException {
        String scanBasePath = "com.mazaiting";
        scan(scanBasePath);
        initBeanInstance();
        //ioc();
        initUrlHandlerMap();
    }

    /**
     * 扫描指定路径下的类
     *
     * @param basePath 基础路径
     */
    private void scan(String basePath) {
        // 获取当前包路径
        URL url = this.getClass().getResource("/" + basePath.replace(".", "/"));
        if (Objects.isNull(url)) {
            System.err.println("初始化 URL 异常");
            return;
        }
        // 基础路径
        String baseFilePath = url.getFile();
        // 基础文件夹
        File baseFile = new File(baseFilePath);
        // 获取文件列表
        String[] subFilePaths = baseFile.list();
        // 遍历
        for (String subFilePath : subFilePaths != null ? subFilePaths : new String[0]) {
            // 构造文件
            File subFile = new File(baseFilePath + subFilePath);
            // 构造路径
            String path = basePath + "." + subFilePath;
            // 如果是路径, 则继续扫描
            if (subFile.isDirectory()) {
                scan(path);
            } else {
                classList.add(path);
            }
        }
    }

    /**
     * 初始化 Bean 实例
     */
    private void initBeanInstance() {
        // 遍历类列表
        for (String beanName : classList) {
            try {
                // 替换.class
                beanName = beanName.replace(".class", "");
                // 加载类
                Class<?> beanClass = Class.forName(beanName);
                try {
                    if (beanClass.isAnnotationPresent(Controller.class)) {
                        // 判断是否有 Controller 注解
                        Controller controller = beanClass.getAnnotation(Controller.class);
                        beanInstanceMap.put(controller.value(), beanClass.newInstance());
                    } else if (beanClass.isAnnotationPresent(Service.class)) {
                        // 判断是否有 Service 注解
                        Service service = beanClass.getAnnotation(Service.class);
                        beanInstanceMap.put(service.value(), beanClass.newInstance());
                    }
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 控制反转 IOC
     */
    private void ioc() {
        // 遍历类列表
        for (String beanName : classList) {
            try {
                // 替换.class
                beanName = beanName.replace(".class", "");
                // 加载类
                Class<?> beanClass = Class.forName(beanName);
                // 获取字段
                Field[] fields = beanClass.getDeclaredFields();
                for (Field field : fields) {
                    // 判断字段是否有 Resource 注解
                    if (field.isAnnotationPresent(Resource.class)) {
                        // 获取名称
                        Resource resource = field.getAnnotation(Resource.class);
                        String val = resource.name();
                        try {
                            field.setAccessible(true);
                            // 判断是否有 Service 注解
                            Service service = beanClass.getAnnotation(Service.class);
                            if (service != null) {
                                field.set(beanInstanceMap.get(service.value()), beanInstanceMap.get(val));
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 初始化 URL
     */
    private void initUrlHandlerMap() {
        // 遍历类
        for (String beanName : classList) {
            try {
                // 替换.class
                beanName = beanName.replace(".class", "");
                // 加载类
                Class<?> beanClass = Class.forName(beanName);
                // 判断是否有 RequestMapping 注解
                if (beanClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping baseReqMap = beanClass.getAnnotation(RequestMapping.class);
                    if (baseReqMap != null) {
                        // 获取路径值
                        String requestBaseUrl = baseReqMap.value();
                        // 获取方法
                        Method[] methods = beanClass.getMethods();
                        // 遍历方法添加子路径
                        for (Method method : methods) {
                            RequestMapping reqMap = method.getAnnotation(RequestMapping.class);
                            if (reqMap != null) {
                                urlHandlerMap.put(requestBaseUrl + reqMap.value(), method);
                            }
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String requestUrl = uri.replace(contextPath, "");
        Method method = urlHandlerMap.get(requestUrl);
        if (Objects.isNull(method)) {
            System.err.println("路径不存在");
            return;
        }
        // 加载当前类
        Class<?> beanClass = method.getDeclaringClass();
        // 获取当前类实例
        Controller controller = beanClass.getAnnotation(Controller.class);
        Object bean = beanInstanceMap.get(controller.value());
        try {
            // 获取参数
            Map<String, String[]> requestParameterMap = req.getParameterMap();
            // 遍历设置参数
            Object[] args = new Object[requestParameterMap.entrySet().size()];
            int index = 0;
            for (Map.Entry<String, String[]> entry : requestParameterMap.entrySet()) {
                args[index] = entry.getValue();
                index++;
            }
            // 执行方法
            Object invoke = method.invoke(bean, args);
            handleResult(method, invoke, resp);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理结果
     * @param method 方法
     * @param invoke 执行结果
     * @param resp 响应
     */
    private void handleResult(Method method, Object invoke, HttpServletResponse resp) {
        // 如果是字符串类型
        if (method.getReturnType() == String.class) {
            try {
                resp.setContentType("application/json; charset=utf-8");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter writer = resp.getWriter();
                writer.write(new String(invoke.toString().getBytes(StandardCharsets.UTF_8)));
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


















